#include <task/task.h>
#include <task/tss.h>
#include <klibc/ctype.h>
#include <mmu/mmu.h>

/*
 * Switcha dal kernel mode all'user mode
 */
void task_switch_to_user_mode()
{
	asm volatile("cli; \
				  mov $0x23, %eax;\
				  mov %ax, %ds;\
				  mov %ax, %es;\
				  mov %ax, %fs;\
				  mov %ax, %gs;\
				  mov %esp, %eax;\
				  pushl $0x23;\
				  pushl %eax;\
				  pushf;\
				  mov $0x200, %eax;\
				  push %eax;\
				  pushl $0x28;\
				  push $1f;\
				  iret;\
				  1:");	
}


/*
 * Inizializza il tasking
 */
void task_init()
{
	// Imposta la funzione utilizzata per lo switching tra i processi
	task_switching_func = &task_switch_round_robin;
	
	task_num = 1;
	
	task_list = NULL;
	task_current = task_list;
	
	// Passa all'user mode
	//task_switch_to_user_mode();
}



/*
 * Si occupa di switchare tra i processi in esecuzione
 */
void task_switch()
{
	// Chiama la funzione selezionata come metodo di switching
	if(task_switching_func != NULL)
	{
		void (*switching)();
		switching = task_switching_func;
		switching();	
	}
}




/*
 * Metodo di switch round robin
 */
void task_switch_round_robin()
{
	if(task_current->next != 0)
		task_current = task_current->next;
	else
		task_current = task_list;
}


/*
 * Metodo di switch che utilizza le priorita'
 */
void task_switch_priority()
{
	/* 
	 * Eseguira' per primi i task con priorita' alta, quando finiranno,
	 * Esegue quelli con quella piu' bassa e cosi' via. Se un processo
	 * rimane inattivo per troppi cicli, viene portato in priorita' superiore
	 */
	//printf("Priority switch: no processes!\n");
}


/*
 * Crea un processo
 */
uint32_t task_fork()
{
	uint32_t pid = task_num;
	task_num++;
	
	
	task_t *t = (task_t *) malloc(sizeof(task_t));
	//*t = *task_current;
	t->term = term_new_default();
	t->next = NULL;
	t->pid = pid;
	strcpy(t->name, task_current->name);
	t->next = task_list;
	task_list = t;
		
	return pid;
}


/*
 * Exit: chiude un processo
 */
void task_exit()
{
	/*task_t *suc = task_current->next;
	
	if(task_current->prev->next != 0)
		task_current->prev->next = suc;
	else
		task_start = suc;
		
	free(task_current);
	task_current = suc;*/
}

/*
 * Output in un task
 */
void task_out(char *txt)
{
	while(*txt != '\0')
	{
		term_putch(task_current->term, *txt);
		txt++;
	}
}

/*
 * Return current task id
 */
uint32_t task_get_pid()
{
	return task_current->pid;
}


/*
 * Carica in memoria un programma e lo esegue
 */
uint32_t task_exec(char *path, char *name, char *argv[], char *env[])
{
	uint32_t pid = task_num;
	task_num++;	
	
	task_t *t = (task_t *) malloc(sizeof(task_t));
	//*t = *task_current;
	t->term = term_new_default();
	t->next = NULL;
	t->pid = pid;
	//t->argv = argv;
	//t->env = env;
	strcpy(t->path, path);
	strcpy(t->name, name);
	t->next = task_list;
	task_list = t;
		
	return pid;		
}


/*
 * 1 se il task esiste
 */
uint8_t task_ifexist(char *name)
{
	
	return 0;
}


void task_print_list()
{
	task_t *t = task_list;
		
	printf("name\tcpu\tmmu\tpriority\n");
	while(t != NULL)
	{
		printf("%s\t%d\t%d\t%d\n",t->name, t->cpu_usage, t->mmu_usage, t->priority);
		t = t->next;			
	}
}
